#include "src/scene/hbb/box.h"

#include <GL/gl.h>
#include <cmath>

#include "src/scene/nearestInfo.h"

void HBBBox::draw(const Color &_color) const{
    //sempre em wireframe
    glDisable(GL_LIGHTING);

    glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
    glDisable(GL_CULL_FACE);

    glEnable(GL_COLOR_MATERIAL);
    glColor4f(_color.red, _color.green, _color.blue, _color.alpha);
    drawBox(min, max);
    glDisable(GL_COLOR_MATERIAL);


    glEnable(GL_LIGHTING);

    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
    glEnable(GL_CULL_FACE);
}

bool HBBBox::hit(const Ray &_ray, NearestInfo *_nearest) const
{
    Vector3D p = _ray.getOrigin();
    Vector3D d = _ray.getDirection();

    double tmin = -INFINITY, tmax = INFINITY;

    for(unsigned int i = 0; i < 3; i++)
    {
        if(fabs(d[i]) < ERROR) //se for igual a zero, é parelelo ao plano
        {
            if(p[i] < min[i] || p[i] > max[i]) return false;
        }
        else
        {
            double t1 = (min[i] - p[i])/d[i];
            double t2 = (max[i] - p[i])/d[i];

            if(t1 > t2)
            {
                double aux = t1;
                t1 = t2;
                t2 = aux;
            }
            if(tmin < t1) tmin = t1;
            if(tmax > t2) tmax = t2;

            if(tmin > tmax) return false;
            if(tmax < 0) return false;
        }
    }

    _nearest->t = tmin;

    return true;
}



void boxPoints(const Vector3D &_min, const Vector3D &_max, Vector3D *_point){

/*
         p6------p7 max
         /|      /|
        p2------p3|
        |p4-----|-p5
        |/      |/
z   min p0------p1
| /y
|/_ _ x
*/
    _point[0] = Vector3D(_min);
    _point[1] = Vector3D(_max.x, _min.y, _min.z);
    _point[2] = Vector3D(_min.x, _min.y, _max.z);
    _point[3] = Vector3D(_max.x, _min.y, _max.z);
    _point[4] = Vector3D(_min.x, _max.y, _min.z);
    _point[5] = Vector3D(_max.x, _max.y, _min.z);
    _point[6] = Vector3D(_min.x, _max.y, _max.z);
    _point[7] = Vector3D(_max);
}

void drawBox(const Vector3D &_min, const Vector3D &_max, bool _useTexture, double _minTexU, double _maxTexU, double _minTexV, double _maxTexV){
    Vector3D point[8];
    boxPoints(_min, _max, point);

//	glPushMatrix();
//	glMultMatrixd(object->getTransformation().getLocalToWorld().toGlMatrix());

    Vector3D normal;
    glBegin(GL_QUADS);

    if(_useTexture)
    {//parte de baixo

        normal = ((point[4] - point[0]).crossProduct(point[1] - point[0])).normalize();
        glNormal3f(normal.x, normal.y, normal.z);
        glTexCoord2d(_minTexU,_maxTexV);
        glVertex3d(point[0].x, point[0].y, point[0].z);
        glTexCoord2d(_minTexU,_minTexV);
        glVertex3d(point[4].x, point[4].y, point[4].z);
        glTexCoord2d(_maxTexU,_minTexV);
        glVertex3d(point[5].x, point[5].y, point[5].z);
        glTexCoord2d(_maxTexU,_maxTexV);
        glVertex3d(point[1].x, point[1].y, point[1].z);


    //parte de cima
        glNormal3f(-normal.x, -normal.y, -normal.z);
        glTexCoord2d(_minTexU,_minTexV);
        glVertex3d(point[2].x, point[2].y, point[2].z);
        glTexCoord2d(_maxTexU,_minTexV);
        glVertex3d(point[3].x, point[3].y, point[3].z);
        glTexCoord2d(_maxTexU,_maxTexV);
        glVertex3d(point[7].x, point[7].y, point[7].z);
        glTexCoord2d(_minTexU,_maxTexV);
        glVertex3d(point[6].x, point[6].y, point[6].z);

    //parte de esquerda
        normal = ((point[2] - point[0]).crossProduct(point[4] - point[0])).normalize();
        glNormal3f(normal.x, normal.y, normal.z);
        glTexCoord2d(_maxTexU,_minTexV);
        glVertex3d(point[0].x, point[0].y, point[0].z);
        glTexCoord2d(_maxTexU,_maxTexV);
        glVertex3d(point[2].x, point[2].y, point[2].z);
        glTexCoord2d(_minTexU,_maxTexV);
        glVertex3d(point[6].x, point[6].y, point[6].z);
        glTexCoord2d(_minTexU,_minTexV);
        glVertex3d(point[4].x, point[4].y, point[4].z);

    //parte de direita
        glNormal3f(-normal.x, -normal.y, -normal.z);
        glTexCoord2d(_minTexU,_minTexV);
        glVertex3d(point[1].x, point[1].y, point[1].z);
        glTexCoord2d(_maxTexU,_minTexV);
        glVertex3d(point[5].x, point[5].y, point[5].z);
        glTexCoord2d(_maxTexU,_maxTexV);
        glVertex3d(point[7].x, point[7].y, point[7].z);
        glTexCoord2d(_minTexU,_maxTexV);
        glVertex3d(point[3].x, point[3].y, point[3].z);

    //parte de frente
        normal = ((point[1] - point[0]).crossProduct(point[2] - point[0])).normalize();
        glNormal3f(normal.x, normal.y, normal.z);
        glTexCoord2d(_minTexU,_minTexV);
        glVertex3d(point[0].x, point[0].y, point[0].z);
        glTexCoord2d(_maxTexU,_minTexV);
        glVertex3d(point[1].x, point[1].y, point[1].z);
        glTexCoord2d(_maxTexU,_maxTexV);
        glVertex3d(point[3].x, point[3].y, point[3].z);
        glTexCoord2d(_minTexU,_maxTexV);
        glVertex3d(point[2].x, point[2].y, point[2].z);

    //parte de tras
        glNormal3f(-normal.x, -normal.y, -normal.z);
        glTexCoord2d(_maxTexU,_minTexV);
        glVertex3d(point[4].x, point[4].y, point[4].z);
        glTexCoord2d(_maxTexU,_maxTexV);
        glVertex3d(point[6].x, point[6].y, point[6].z);
        glTexCoord2d(_minTexU,_maxTexV);
        glVertex3d(point[7].x, point[7].y, point[7].z);
        glTexCoord2d(_minTexU,_minTexV);
        glVertex3d(point[5].x, point[5].y, point[5].z);
    }
    else
    {
    //parte de baixo
        normal = ((point[4] - point[0]).crossProduct(point[1] - point[0])).normalize();
        glNormal3f(normal.x, normal.y, normal.z);
        glVertex3d(point[0].x, point[0].y, point[0].z);
        glVertex3d(point[4].x, point[4].y, point[4].z);
        glVertex3d(point[5].x, point[5].y, point[5].z);
        glVertex3d(point[1].x, point[1].y, point[1].z);


    //parte de cima
        glNormal3f(-normal.x, -normal.y, -normal.z);
        glVertex3d(point[2].x, point[2].y, point[2].z);
        glVertex3d(point[3].x, point[3].y, point[3].z);
        glVertex3d(point[7].x, point[7].y, point[7].z);
        glVertex3d(point[6].x, point[6].y, point[6].z);

    //parte de esquerda
        normal = ((point[2] - point[0]).crossProduct(point[4] - point[0])).normalize();
        glNormal3f(normal.x, normal.y, normal.z);
        glVertex3d(point[0].x, point[0].y, point[0].z);
        glVertex3d(point[2].x, point[2].y, point[2].z);
        glVertex3d(point[6].x, point[6].y, point[6].z);
        glVertex3d(point[4].x, point[4].y, point[4].z);

    //parte de direita
        glNormal3f(-normal.x, -normal.y, -normal.z);
        glVertex3d(point[1].x, point[1].y, point[1].z);
        glVertex3d(point[5].x, point[5].y, point[5].z);
        glVertex3d(point[7].x, point[7].y, point[7].z);
        glVertex3d(point[3].x, point[3].y, point[3].z);

    //parte de frente
        normal = ((point[1] - point[0]).crossProduct(point[2] - point[0])).normalize();
        glNormal3f(normal.x, normal.y, normal.z);
        glVertex3d(point[0].x, point[0].y, point[0].z);
        glVertex3d(point[1].x, point[1].y, point[1].z);
        glVertex3d(point[3].x, point[3].y, point[3].z);
        glVertex3d(point[2].x, point[2].y, point[2].z);

    //parte de tras
        glNormal3f(-normal.x, -normal.y, -normal.z);
        glVertex3d(point[4].x, point[4].y, point[4].z);
        glVertex3d(point[6].x, point[6].y, point[6].z);
        glVertex3d(point[7].x, point[7].y, point[7].z);
        glVertex3d(point[5].x, point[5].y, point[5].z);
    }
    glEnd();

//	glPopMatrix();

}
